package cn.sunxiansheng.minio.utils;

import cn.sunxiansheng.minio.config.properties.MinioProperties;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * Description: Minio工具类
 *
 * @Author sun
 * @Create 2024/11/13 09:49
 * @Version 1.0
 */
@Component
public class MinioUtil {

    @Resource
    private MinioClient minioClient;

    @Resource
    private MinioProperties minioProperties;

    /**
     * 检查存储桶是否存在
     *
     * @param bucketName
     * @return
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException("检查存储桶是否存在失败", e);
        }
    }

    /**
     * 列出所有存储桶的名字
     *
     * @return
     */
    public List<String> listBucketNames() {
        try {
            return minioClient.listBuckets().stream().map(Bucket::name).collect(Collectors.toList());
        } catch (Exception e) {
            throw new RuntimeException("列出所有存储桶的名字失败", e);
        }
    }

    /**
     * 根据名字创建存储桶
     *
     * @param bucketName
     */
    public void makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException("创建存储桶失败", e);
        }
    }

    /**
     * 根据名字移除一个空桶
     *
     * @param bucketName
     */
    public void removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            throw new RuntimeException("移除存储桶失败", e);
        }
    }

    /**
     * 下载对象到指定位置
     *
     * @param bucketName
     * @param objectName
     * @param fileName
     */
    public void downloadObject(String bucketName, String objectName, String fileName) {
        try {
            minioClient.downloadObject(DownloadObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .filename(fileName)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException("下载对象失败", e);
        }
    }

    /**
     * 文件上传并返回预览url和下载url
     *
     * @param file
     * @param bucketName
     * @return 第一个元素为预览url，第二个元素为下载url
     */
    public List<String> putObject(MultipartFile file, String bucketName) {
        try {
            // 获取文件名
            String fileName = file.getOriginalFilename();
            // 获取文件输入流
            InputStream inputStream = file.getInputStream();
            // 获取当前日期，构建文件夹路径
            String dateFolder = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
            // 使用 UUID 生成唯一标识，防止重复
            String uniqueName = UUID.randomUUID().toString();
            // 构造对象名称：日期路径 + 唯一标识 + 文件名
            String objectName = dateFolder + "/" + uniqueName + "-" + fileName;
            // 获取文件的 MIME 类型，如果不确定，则默认设置为 "application/octet-stream"
            String contentType = file.getContentType() != null ? file.getContentType() : "application/octet-stream";
            // 执行上传操作，不指定大小并分块上传，块大小为 10 MB
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, -1, 10 * 1024 * 1024)
                            .contentType(contentType)
                            .build()
            );
            // 获取预览url
            String previewUrl = generatePreviewLink(bucketName, objectName);
            // 获取下载url
            String downloadUrl = generateDownloadLink(bucketName, objectName);
            return Arrays.asList(previewUrl, downloadUrl);
        } catch (Exception e) {
            throw new RuntimeException("上传对象失败", e);
        }
    }

    /**
     * 删除指定前缀的所有对象（文件或文件夹）
     *
     * @param bucketName     桶名称
     * @param prefix     对象前缀（文件夹路径或文件名）
     * @return 删除结果
     */
    public String removeObjectOrFolder(String bucketName, String prefix) {
        try {
            // 递归列出该前缀下的所有对象
            Iterable<Result<Item>> objects = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .recursive(true)
                            .build());

            // 遍历并删除前缀下的所有对象
            for (Result<Item> result : objects) {
                String objectName = result.get().objectName();
                minioClient.removeObject(
                        RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(objectName)
                                .build()
                );
            }
            return "All objects under " + prefix + " removed successfully!";
        } catch (Exception e) {
            e.printStackTrace();
            return "Failed to remove objects under " + prefix;
        }
    }

    /**
     * 获取下载url
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public String generateDownloadLink(String bucketName, String objectName) {
        String endpoint = minioProperties.getEndpoint();
        // 设置文件的下载链接
        return String.format("%s/%s/%s?response-content-disposition=attachment", endpoint, bucketName, objectName);
    }

    /**
     * 获取预览url
     *
     * @param bucketName
     * @param objectName
     * @return
     */
    public String generatePreviewLink(String bucketName, String objectName) {
        String endpoint = minioProperties.getEndpoint();
        // 返回文件的预览链接
        return String.format("%s/%s/%s", endpoint, bucketName, objectName);
    }
}